// list_casts.cpp
//
// List Rivendell Casts
//
//   (C) Copyright 2002-2007 Fred Gleason <fredg@paravelsystems.com>
//
//      $Id: list_casts.cpp,v 1.6 2007/10/19 17:51:46 fredg Exp $
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License version 2 as
//   published by the Free Software Foundation.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public
//   License along with this program; if not, write to the Free Software
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//

#include <math.h>
#include <stdlib.h>
#include <unistd.h>

#include <qdialog.h>
#include <qstring.h>
#include <qpushbutton.h>
#include <qlistbox.h>
#include <qtextedit.h>
#include <qlabel.h>
#include <qpainter.h>
#include <qevent.h>
#include <qmessagebox.h>
#include <qbuttongroup.h>
#include <qdatetime.h>
#include <qfile.h>
#include <qprogressdialog.h>
#include <qapplication.h>
#include <qfiledialog.h>

#include <rddb.h>
#include <rdpodcast.h>
#include <rdtextfile.h>
#include <rdcut_dialog.h>
#include <rdconf.h>
#include <rdcut.h>
#include <rdwavefile.h>
#include <rdfeed.h>
#include <rdescape_string.h>
#include <rdurl.h>

#include <list_casts.h>
#include <edit_cast.h>
#include <globals.h>

//
// Icons
//
#include "../icons/redball.xpm"
#include "../icons/greenball.xpm"


ListCasts::ListCasts(unsigned feed_id,QWidget *parent,const char *name)
  : QDialog(parent,name,true)
{
  list_feed_id=feed_id;

  //
  // Fix the Window Size
  //
  setMinimumWidth(sizeHint().width());
  setMinimumHeight(sizeHint().height());

  setCaption(tr("Podcast List"));

  //
  // Create Fonts
  //
  QFont font=QFont("Helvetica",12,QFont::Bold);
  font.setPixelSize(12);
  QFont list_font=QFont("Helvetica",12,QFont::Normal);
  list_font.setPixelSize(12);
  QFont small_font=QFont("Helvetica",10,QFont::Normal);
  small_font.setPixelSize(10);

  //
  //  Post Cart Button
  //
  list_cart_button=new QPushButton(this,"list_cart_button");
  list_cart_button->setFont(font);
  list_cart_button->setText(tr("Post From\nCar&t/Cut"));
  connect(list_cart_button,SIGNAL(clicked()),this,SLOT(addCartData()));

  //
  //  Post File Button
  //
  list_file_button=new QPushButton(this,"list_file_button");
  list_file_button->setFont(font);
  list_file_button->setText(tr("Post From\n&File"));
  connect(list_file_button,SIGNAL(clicked()),this,SLOT(addFileData()));

  //
  //  Edit Button
  //
  list_edit_button=new QPushButton(this,"list_edit_button");
  list_edit_button->setFont(font);
  list_edit_button->setText(tr("&Edit"));
  connect(list_edit_button,SIGNAL(clicked()),this,SLOT(editData()));

  //
  //  Delete Button
  //
  list_delete_button=new QPushButton(this,"list_delete_button");
  list_delete_button->setFont(font);
  list_delete_button->setText(tr("&Delete"));
  connect(list_delete_button,SIGNAL(clicked()),this,SLOT(deleteData()));

  //
  //  Close Button
  //
  list_close_button=new QPushButton(this,"list_close_button");
  list_close_button->setDefault(true);
  list_close_button->setFont(font);
  list_close_button->setText(tr("&Close"));
  connect(list_close_button,SIGNAL(clicked()),this,SLOT(closeData()));

  //
  // Create Icons
  //
  list_greenball_map=new QPixmap(greenball_xpm);
  list_redball_map=new QPixmap(redball_xpm);

  //
  // Group List
  //
  list_casts_view=new RDListView(this,"list_casts_view");
  list_casts_view->setAllColumnsShowFocus(true);
  list_casts_view->setItemMargin(5);
  list_casts_view->addColumn(tr(" "));
  list_casts_view->setColumnAlignment(0,AlignCenter);
  list_casts_view->addColumn(tr("Title"));
  list_casts_view->setColumnAlignment(1,AlignLeft);
  list_casts_view->addColumn(tr("Origin"));
  list_casts_view->setColumnAlignment(2,AlignLeft);
  list_casts_view->addColumn(tr("Expires"));
  list_casts_view->setColumnAlignment(3,AlignCenter);
  list_casts_view->addColumn(tr("Length"));
  list_casts_view->setColumnAlignment(4,AlignRight);
  list_casts_view->addColumn(tr("Description"));
  list_casts_view->setColumnAlignment(5,AlignLeft);
  list_casts_view->addColumn(tr("Category"));
  list_casts_view->setColumnAlignment(6,AlignCenter);
  list_casts_view->addColumn(tr("Link"));
  list_casts_view->setColumnAlignment(7,AlignCenter);
  QLabel *list_box_label=new QLabel(list_casts_view,tr("&Casts:"),
				    this,"list_box_label");
  list_box_label->setGeometry(14,11,85,19);
  connect(list_casts_view,
	  SIGNAL(doubleClicked(QListViewItem *,const QPoint &,int)),
	  this,
	  SLOT(doubleClickedData(QListViewItem *,const QPoint &,int)));

  RefreshList();
  connect(cast_ripc,SIGNAL(userChanged()),this,SLOT(userChangedData()));
  userChangedData();
}


ListCasts::~ListCasts()
{
}


QSize ListCasts::sizeHint() const
{
  return QSize(640,480);
} 


QSizePolicy ListCasts::sizePolicy() const
{
  return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
}


void ListCasts::addCartData()
{
  QString cutname;
  QString tmpfile;
  QString destfile;
  QString sql;
  RDSqlQuery *q;
  RDPodcast *cast=NULL;

  RDCutDialog *cd=new RDCutDialog(&cutname,&cast_filter,&cast_group,
				  cast_ripc->user());
  if(cd->exec()==0) {
    QProgressDialog *pd=
      new QProgressDialog(tr("Uploading Audio..."),"Cancel",2,this);
    pd->setCaption(tr("Progress"));
    pd->setMinimumDuration(0);
    pd->setProgress(0);
    qApp->processEvents();
    sleep(1);
    qApp->processEvents();
    QString cut_filename=RDCut::pathName(cutname); 
    QString cmd=GetExportCommand(cut_filename,&tmpfile);
    if(system(cmd)!=0) {
      pd->reset();
      QMessageBox::warning(this,tr("Format Error"),
			   tr("Unable to convert audio file!"));
      delete pd;
      delete cd;
      return;
    }
    if(pd->wasCanceled()) {
      delete pd;
      delete cd;
      return;
    }
    pd->setProgress(1);
    qApp->processEvents();
    QFile file(tmpfile);
    int length=file.size();
    RDCut *cut=new RDCut(cutname);
    unsigned cast_id=CreateCast(&destfile,length,cut->length());
    delete cut;
    cast=new RDPodcast(cast_id);
    cmd=cast->audioUploadCommand(tmpfile);
    if(system(cmd)!=0) {
      pd->reset();
      QMessageBox::warning(this,tr("Upload Error"),
			   tr("Unable to upload audio file!"));
      sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id);
      q=new RDSqlQuery(sql);
      delete q;
      delete cast;
      delete pd;
      delete cd;
      return;
    }
    pd->setProgress(2);
    pd->reset();
    delete pd;
    EditCast *edit_cast=new EditCast(cast_id,this);
    edit_cast->exec();
    RDListViewItem *item=new RDListViewItem(list_casts_view);
    item->setId(cast_id);
    RefreshItem(item);
    list_casts_view->setSelected(item,true);
    list_casts_view->ensureItemVisible(item);
    delete edit_cast;
  }
  delete cast;
  delete cd;
  unlink(tmpfile);
}


void ListCasts::addFileData()
{
  QString sql;
  RDSqlQuery *q;
  QString cmd;
  QString tmpfile;
  QString tmpfile2;
  QString destfile;
  int time_length;
  QString srcfile=QFileDialog::getOpenFileName("",RD_AUDIO_FILE_FILTER,this);
  if(srcfile.isNull()) {
    return;
  }
  QProgressDialog *pd=
    new QProgressDialog(tr("Uploading Audio..."),"Cancel",4,this);
  pd->setCaption(tr("Progress"));
  pd->setMinimumDuration(0);
  pd->setProgress(0);
  qApp->processEvents();
  sleep(1);
  pd->setProgress(1);
  qApp->processEvents();
  cmd=GetImportCommand(srcfile,&tmpfile);
  if(system(cmd)!=0) {
    pd->reset();
    QMessageBox::warning(this,tr("Format Error"),
			 tr("Unable to convert audio file!"));
    delete pd;
    return;
  }
  pd->setProgress(2);
  qApp->processEvents();
  cmd=GetExportCommand(tmpfile,&tmpfile2);
  if(system(cmd)!=0) {
    pd->reset();
    QMessageBox::warning(this,tr("Format Error"),
			 tr("Unable to convert audio file!"));
    delete pd;
    return;
  }
  RDWaveFile *wave=new RDWaveFile(tmpfile2);
  if(!wave->openWave()) {
    pd->reset();
    QMessageBox::warning(this,tr("Format Error"),
			 tr("Unable to convert audio file!"));
    delete pd;
    delete wave;
    return;
  }
  time_length=wave->getExtTimeLength();
  delete wave;
  pd->setProgress(3);
  qApp->processEvents();  
  QFile file(tmpfile2);
  int length=file.size();
  unsigned cast_id=CreateCast(&destfile,length,time_length);
  RDPodcast *cast=new RDPodcast(cast_id);
  cmd=cast->audioUploadCommand(tmpfile2);
  if(system(cmd)!=0) {
    pd->reset();
    QMessageBox::warning(this,tr("Upload Error"),
			 tr("Unable to upload audio file!"));
    sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id);
    q=new RDSqlQuery(sql);
    delete q;
    delete cast;
    delete pd;
    return;
  }
  pd->reset();
  delete pd;
  EditCast *edit_cast=new EditCast(cast_id,this);
  edit_cast->exec();
  RDListViewItem *item=new RDListViewItem(list_casts_view);
  item->setId(cast_id);
  RefreshItem(item);
  list_casts_view->setSelected(item,true);
  list_casts_view->ensureItemVisible(item);
  delete edit_cast;
  delete cast;
  unlink(tmpfile);
  unlink(tmpfile2);
}


void ListCasts::editData()
{
  RDListViewItem *item=(RDListViewItem *)list_casts_view->selectedItem();
  if(item==NULL) {
    return;
  }
  EditCast *edit_cast=new EditCast(item->id(),this);
  if(edit_cast->exec()==0) {
    RefreshItem(item);
  }
  delete edit_cast;
}


void ListCasts::deleteData()
{
  QString sql;
  RDSqlQuery *q;

  RDListViewItem *item=(RDListViewItem *)list_casts_view->selectedItem();
  if(item==NULL) {
    return;
  }
  if(QMessageBox::question(this,tr("Delete Podcast"),
			   tr("Are you sure you want to delete this podcast?"),
			   QMessageBox::Yes,QMessageBox::No)==
     QMessageBox::No) {
    return;
  }

  QProgressDialog *pd=
    new QProgressDialog(tr("Deleting Podcast..."),"Cancel",2,this);
  pd->setCaption(tr("Progress"));
  pd->setMinimumDuration(0);
  pd->setProgress(0);
  qApp->processEvents();
  sleep(1);
  qApp->processEvents();
  RDPodcast *cast=new RDPodcast(item->id());
  QString cmd=cast->audioPurgeCommand();
  if(cmd.isEmpty()) {
    pd->reset();
    QMessageBox::warning(this,tr("Internal Error"),
			 tr("Unable to generate purge command!"));
    delete pd;
    delete cast;
    return;
  }
  if(system(cmd)!=0) {
    pd->reset();
    QMessageBox::warning(this,tr("Remote Error"),
			 tr("Unable to delete remote audio!"));
    delete pd;
    delete cast;
    return;
  }
  pd->setProgress(1);
  qApp->processEvents();

  sql=QString().sprintf("delete from PODCASTS where ID=%u",item->id());
  q=new RDSqlQuery(sql);
  delete q;
  sql=QString().sprintf("update FEEDS set LAST_BUILD_DATETIME=now() \
                        where ID=%u",list_feed_id);
  q=new RDSqlQuery(sql);
  delete q;

  pd->reset();

  delete pd;
  delete cast;
  delete item;
}


void ListCasts::doubleClickedData(QListViewItem *item,const QPoint &pt,
				   int col)
{
  editData();
}


void ListCasts::userChangedData()
{
  list_cart_button->setEnabled(cast_user->addPodcast());
  list_file_button->setEnabled(cast_user->addPodcast());
  list_edit_button->setEnabled(cast_user->editPodcast());
  list_delete_button->setEnabled(cast_user->deletePodcast());
}


void ListCasts::closeData()
{
  done(0);
}


void ListCasts::resizeEvent(QResizeEvent *e)
{
  list_cart_button->setGeometry(10,size().height()-60,80,50);
  list_file_button->setGeometry(100,size().height()-60,80,50);
  list_edit_button->setGeometry(190,size().height()-60,80,50);
  list_delete_button->setGeometry(280,size().height()-60,80,50);
  list_close_button->setGeometry(size().width()-90,size().height()-60,80,50);
  list_casts_view->setGeometry(10,30,size().width()-20,size().height()-100);
}


void ListCasts::RefreshList()
{
  QString sql;
  RDSqlQuery *q;
  RDListViewItem *item;

  list_casts_view->clear();
  sql=QString().sprintf("select ID from PODCASTS where FEED_ID=%d \
                         order by ORIGIN_DATETIME",list_feed_id);
  q=new RDSqlQuery(sql);
  while (q->next()) {
    item=new RDListViewItem(list_casts_view);
    item->setId(q->value(0).toInt());
    RefreshItem(item);
  }
  delete q;
}


void ListCasts::RefreshItem(RDListViewItem *item)
{
  QString sql;
  RDSqlQuery *q;

  sql=QString().sprintf("select STATUS,ITEM_TITLE,ORIGIN_DATETIME,SHELF_LIFE,\
                         AUDIO_TIME,ITEM_DESCRIPTION,ITEM_CATEGORY,ITEM_LINK \
                         from PODCASTS where ID=%d",item->id());
  q=new RDSqlQuery(sql);
  if(q->first()) {
    if(q->value(0).toUInt()==RDPodcast::StatusActive) {
      item->setPixmap(0,*list_greenball_map);
    }
    else {
      item->setPixmap(0,*list_redball_map);
    }
    item->setText(1,q->value(1).toString());
    item->setText(2,q->value(2).toDateTime().toString("MM/dd/yyyy hh:mm:ss"));
    if(q->value(3).toInt()==0) {
      item->setText(3,tr("Never"));
    }
    else {
      item->setText(3,q->value(2).toDateTime().addDays(q->value(3).toInt()).
		    toString("MM/dd/yyyy"));
    }
    item->setText(4,RDGetTimeLength(q->value(4).toInt(),false,false));
    item->setText(5,q->value(5).toString());
    item->setText(6,q->value(6).toString());
    item->setText(7,q->value(7).toString());
  }
  delete q;
}


unsigned ListCasts::CreateCast(QString *filename,int bytes,int msecs)
{
  QString sql;
  RDSqlQuery *q;
  RDSqlQuery *q1;
  unsigned cast_id=0;
  QDateTime current_datetime=
    QDateTime(QDate::currentDate(),QTime::currentTime());

  sql=QString().sprintf("select CHANNEL_TITLE,CHANNEL_DESCRIPTION,\
                         CHANNEL_CATEGORY,CHANNEL_LINK,MAX_SHELF_LIFE,\
                         UPLOAD_FORMAT from FEEDS \
                         where ID=%u",list_feed_id);
  q=new RDSqlQuery(sql);
  if(!q->first()) {
    delete q;
    return 0;
  }

  //
  // Create Entry
  //
  sql=QString().sprintf("insert into PODCASTS set \
                         FEED_ID=%u,\
                         ITEM_TITLE=\"%s\",\
                         ITEM_DESCRIPTION=\"%s\",\
                         ITEM_CATEGORY=\"%s\",\
                         ITEM_LINK=\"%s\",\
                         SHELF_LIFE=%d,\
                         ORIGIN_DATETIME=\"%s\"",
			list_feed_id,
			(const char *)RDEscapeString(q->value(0).toString()),
			(const char *)RDEscapeString(q->value(1).toString()),
			(const char *)RDEscapeString(q->value(2).toString()),
			(const char *)RDEscapeString(q->value(3).toString()),
			q->value(4).toInt(),
			(const char *)current_datetime.
			toString("yyyy-MM-dd hh:mm:ss"));
  q1=new RDSqlQuery(sql);
  delete q1;

  //
  // Get the ID
  //
  sql=QString().sprintf("select ID from PODCASTS \
                         where (FEED_ID=%u)&&(ORIGIN_DATETIME=\"%s\")",
			list_feed_id,
			(const char *)current_datetime.
			toString("yyyy-MM-dd hh:mm:ss"));
  q1=new RDSqlQuery(sql);
  if(q1->first()) {
    cast_id=q1->value(0).toUInt();
  }
  delete q1;

  //
  // Generate the Filename
  //
  *filename=QString().
    sprintf("%s.%s",
	    (const char *)QString().sprintf("%06u_%06u",list_feed_id,cast_id),
	    (const char *)RDSettings::defaultExtension((RDSettings::Format)
						       q->value(5).toInt()));
  sql=QString().sprintf("update PODCASTS set AUDIO_FILENAME=\"%s\",\
                         AUDIO_LENGTH=%d,\
                         AUDIO_TIME=%d where ID=%u",
			(const char *)(*filename),
			bytes,msecs,cast_id);
  q1=new RDSqlQuery(sql);
  delete q1;
  delete q;
  return cast_id;
}


QString ListCasts::GetImportCommand(const QString &srcfile,QString *destfile)
{
  int format_in=0;
  int temp_length;
  int finished_length;
  QString cmd;

  if(!QFile::exists(srcfile)) {
    QMessageBox::warning(this,tr("File Error"),tr("File does not exist!"));
    return QString();
  }
  RDWaveFile *wave=new RDWaveFile(srcfile);
  if(!wave->openWave()) {
    QMessageBox::warning(this,tr("File Error"),tr("Cannot open file!"));
    delete wave;
    return QString();
  }
  if(wave->type()==RDWaveFile::Unknown) {
    QMessageBox::warning(this,tr("File Error"),tr("Unsupported file type!"));
    wave->closeWave();
    delete wave;
    return QString();
  }
  int samplerate=wave->getSamplesPerSec();
  switch(wave->getFormatTag()) {
      case WAVE_FORMAT_PCM:
	format_in=0;
	temp_length=wave->getSampleLength()*
	  wave->getChannels()*(wave->getBitsPerSample()/8);
	break;

      case WAVE_FORMAT_MPEG:
	format_in=wave->getHeadLayer();
	temp_length=wave->getSampleLength()*wave->getChannels()*2;
	break;

      case WAVE_FORMAT_FLAC:
	format_in=0;
	temp_length=wave->getSampleLength()*wave->getChannels()*2;
	break;
  }
  delete wave;

  RDFeed *feed=new RDFeed(list_feed_id);

  int lib_fmt=0;
  finished_length=
    (int)(((double)temp_length/2.0)*
	  (double)feed->uploadChannels()*
	  ((double)feed->uploadSampleRate())/44100.0);
  lib_fmt=0;
  *destfile=GetTempFilename();
  QString tempwav_name=*destfile+".wav";
  QString tempdat_name=*destfile+".dat";

  float normal=0.0;
  normal=pow(10.0,(double)(-1.0/20.0));
  cmd=QString().
    sprintf("rd_import_file %6.4f %d %d %s %d %d %d %d %s %s %s",
	    normal,
	    format_in,
	    samplerate,
	    (const char *)RDEscapeString(srcfile.utf8()),  
	    lib_fmt,
	    feed->uploadChannels(),
	    feed->uploadSampleRate(),
	    (feed->uploadChannels())*feed->uploadBitRate()/1000,
	    (const char *)(*destfile),
	    (const char *)tempdat_name,
	    (const char *)tempwav_name);
  // printf("CMD: %s\n",(const char *)cmd);

  delete feed;

  return cmd;
}


QString ListCasts::GetExportCommand(const QString &srcfile,QString *destfile)
{
  int format_in=0;
  int len;

  RDFeed *feed=new RDFeed(list_feed_id);
  *destfile=GetTempFilename();
  QFile file(srcfile);
  if(!file.exists()) {
    return QString();
  }
  RDWaveFile *wave=new RDWaveFile(srcfile);
  if(!wave->openWave()) {
    delete wave;
    return QString();
  }
  int samplerate=wave->getSamplesPerSec();
  switch(wave->getFormatTag()) {
      case WAVE_FORMAT_PCM:
	format_in=0;
	len=wave->getSampleLength()*wave->getChannels()*
	  (wave->getBitsPerSample()/8);
	break;

      case WAVE_FORMAT_MPEG:
	format_in=wave->getHeadLayer();
	len=wave->getSampleLength()*wave->getChannels()*2;
	break;
  }
  wave->closeWave();
  delete wave;

  QString cmd;
  float normal=0.0;
  if(feed->normalizeLevel()<=0) {
    normal=pow(10.0,(double)(feed->normalizeLevel()/2000.0));
    cmd=QString().
      sprintf("rd_export_file %6.4f %d %d %s %d %d %d %d %d %s %s.dat %s.%s",
	      normal,
	      format_in,
	      samplerate,
	      (const char *)srcfile,
	      feed->uploadFormat(),
	      feed->uploadChannels(),
	      feed->uploadSampleRate(),
	      feed->uploadBitRate()/1000,
	      feed->uploadQuality(),
	      (const char *)RDEscapeString(*destfile),
	      (const char *)(*destfile),
	      (const char *)(*destfile),
	      RDConfiguration()->audioExtension().ascii());
  }
  else {
    cmd=QString().
      sprintf("rd_export_file 0 %d %d %s %d %d %d %d %d %s %s.dat %s.%s",
	      format_in,
	      samplerate,
	      (const char *)srcfile,
	      feed->uploadFormat(),
	      feed->uploadChannels(),
	      feed->uploadSampleRate(),
	      feed->uploadBitRate()/1000,
	      feed->uploadQuality(),
	      (const char *)RDEscapeString(*destfile),
	      (const char *)(*destfile),
	      (const char *)(*destfile),
	      RDConfiguration()->audioExtension().ascii());
  }
  // LogLine(QString().sprintf("CMD: %s",(const char *)cmd));

  delete feed;

  return cmd;
}


QString ListCasts::GetTempFilename()
{
  char tempname[PATH_MAX];

  sprintf(tempname,"%s/podcastXXXXXX",(const char *)RDTempDir());
  if(mkstemp(tempname)<0) {
    return QString();
  }

  return QString(tempname);
}
